2014_blom_noahs_ark.py

#

SPDX-FileCopyrightText: 2014 Tom Pariente SPDX-FileCopyrightText: 2024 AlICe laboratory https://alicelab.be

SPDX-License-Identifier: GPL-3.0-or-later

#

---------------------------------------------------------------------------------------------------------------# Script développé sous Blender hash:9337574 / MacOSX 10.10.1 Piet Blom / Noah’s Ark / Réinterpretation / Tom Pariente / V09 ---------------------------------------------------------------------------------------------------------------#

import bpy
import random
#
def visibiliteCalques(
    OnOff,
):  # On définit une fonction qui permet d'afficher ou d'éteindre tous les calques.
    bpy.context.scene.layers[0] = OnOff
    bpy.context.scene.layers[1] = OnOff
    bpy.context.scene.layers[2] = OnOff
    bpy.context.scene.layers[3] = OnOff


visibiliteCalques(True)  # On affiche tous les calques.

bpy.ops.object.select_all(
    action="SELECT"
)  # Suppression de la génération précédente, sur tous les calques utilisés.
bpy.ops.object.delete(use_global=False)

bpy.context.scene.layers[1] = (
    False  # On éteint les calques 1, 2 et 3 pour travailler sur le calque 0.
)
bpy.context.scene.layers[2] = False
bpy.context.scene.layers[3] = False
#

---------------------------------------------------------------------------------------------------------------#

GENERATION_DE_L’ETAGE_CREUSE(INVARIANT)_

---------------------------------------------------------------------------------------------------------------# Generation de forme par vertices & faces :

mesVertices = [
    (19, -5, -1.5),
    (21, -5, -1.5),
    (21, -29, -1.5),
    (60, -29, -1.5),
    (60, -31, -1.5),
    (11, -31, -1.5),
    (11, -70, -1.5),
    (9, -70, -1.5),
    (9, -21, -1.5),
    (-5, -21, -1.5),
    (-5, -19, -1.5),
    (19, -19, -1.5),
    (19, -21, -1.5),
    (19, -29, -1.5),
    (11, -29, -1.5),
    (11, -21, -1.5),
    (19, -5, -0.5),
    (21, -5, -0.5),
    (21, -29, -0.5),
    (60, -29, -0.5),
    (60, -31, -0.5),
    (11, -31, -0.5),
    (11, -70, -0.5),
    (9, -70, -0.5),
    (9, -21, -0.5),
    (-5, -21, -0.5),
    (-5, -19, -0.5),
    (19, -19, -0.5),
    (19, -21, -0.5),
    (19, -29, -0.5),
    (11, -29, -0.5),
    (11, -21, -0.5),
    (35, -45, -0.5),
    (-5, -45, -0.5),
    (-5, -5, -0.5),
    (35, -5, -0.5),
]
mesFaces = [
    (1, 2, 18, 17),
    (2, 3, 19, 18),
    (4, 5, 21, 20),
    (5, 6, 22, 21),
    (7, 8, 24, 23),
    (8, 9, 25, 24),
    (10, 11, 27, 26),
    (11, 0, 16, 27),
    (12, 13, 29, 28),
    (13, 14, 30, 29),
    (14, 15, 31, 30),
    (15, 12, 28, 31),
    (0, 1, 2, 13),
    (3, 4, 5, 14),
    (6, 7, 8, 15),
    (9, 10, 11, 12),
]
#

On définit les positions des différents points utilisés, ainsi que les faces à matérialiser en appelant les points qui les composent.

monMesh = bpy.data.meshes.new(
    "cubeMesh"
)  # L'objet est appelé et matérialisé dans la scène.
monObjet = bpy.data.objects.new("cube", monMesh)
maScene = bpy.context.scene
maScene.objects.link(monObjet)
monMesh.from_pydata(mesVertices, [], mesFaces)
#

Duplication, rotation et translation ---------------------------------------------------------------------------------------------------------------#

bpy.ops.object.select_all(
    action="SELECT"
)  # Precedement, seul 1/4 de l'étage creusé (invariant) a été matérialisé, il faut donc le dupliquer. On sélectionne d'abord...
bpy.ops.object.duplicate(
    linked=False, mode="TRANSLATION"
)  # ...puis on duplique l'objet...
bpy.ops.transform.rotate(
    value=1.5708, axis=(0, 0, 1), constraint_axis=(False, False, True)
)  # ...rotation...
bpy.ops.object.duplicate(
    linked=False, mode="TRANSLATION"
)  # ...opération répétée 3 fois pour avoir les 4 pièces nécessaires à l'étage creusé...
bpy.ops.transform.rotate(
    value=1.5708, axis=(0, 0, 1), constraint_axis=(False, False, True)
)
bpy.ops.object.duplicate(linked=False, mode="TRANSLATION")
bpy.ops.transform.rotate(
    value=1.5708, axis=(0, 0, 1), constraint_axis=(False, False, True)
)

bpy.ops.object.select_all(
    action="SELECT"
)  # On finit cet étage en sélectionnant tout...
bpy.ops.group.create()  # On groupe les 4 éléments qui le composent...

bpy.ops.transform.resize(value=(0.5, 0.5, 1))  # On redimensionne l'étage...
bpy.context.scene.layers[1] = (
    True  # Puis on désactive le calque 0 pour travailler sur le n°1, calque du prochain étage.
)
bpy.context.scene.layers[0] = False
#

---------------------------------------------------------------------------------------------------------------#

PREMIERE_BOITE

---------------------------------------------------------------------------------------------------------------#

#
def MaBoite(
    pos_X, pos_Y, pos_Z, dim_X, dim_Y, dim_Z
):  # Ici, une fonction est définie, permettant de créer la première boite de l'étage en cours.
    bpy.ops.mesh.primitive_cube_add(
        location=(pos_X, pos_Y, pos_Z)
    )  # Cette fonction est définie par la position de la boite et ses dimensions.
    bpy.ops.transform.resize(value=(0.5, 0.5, 0.5))
    bpy.ops.transform.resize(value=(dim_X, dim_Y, dim_Z))
    ob = bpy.context.object
    ob.select = True
#

On donne ensuite les différentes dimensions que peuvent avoir les boites qui composent le modèle.

courte_X = (4, 1, 1)  # Barre de 4 de longueur suivant x
courte_Y = (1, 4, 1)  # Barre de 4 de longueur suivant y
longue_X = (9, 1, 1)  # Barre de 9 de longueur suivant x
longue_Y = (1, 9, 1)  # Barre de 9 de longueur suivant y

size_X = [
    courte_X,
    longue_X,
]  # On définit des listes comprenant, pour la première les barres suivant X...
size_Y = [courte_Y, longue_Y]  # Et pour la seconde, les barres suivant Y.

s = random.choice(
    size_X
)  # On donne au script la possibilité de générer une barre parmi 2 possibles, de manière aléatoire.
#

---------------------------------------------------------------------------------------------------------------#

DEUXIEME_BOITE_DONT_LA_POSITION_EST_RELATIVE_A_LA_PREMIERE

Ici, nous allons dire au logiciel quelles sont ses options, en fonction de la première barre qu’il a généré. ---------------------------------------------------------------------------------------------------------------#

#

Par rapport à la première boite générée, les suivantes ne peuvent se positionner qu’à certains endroits précis, que ne définissons ici par leurs coordonnés… Ancrages possible if s == courte_X: #Si la première boite générée est une courte suivant X, les points d’ancrage possible sont…

anc1 = (2, 0, 0)
anc2 = (0.5, 0.5, 0)
anc3 = (-2, 0, 0)
anc4 = (-0.5, -0.5, 0)
locationCX = [
    anc1,
    anc2,
    anc3,
    anc4,
]  # On définit une liste comprenant les différents points d'ancrage possibles.
#

Ancrages possibles if s == longue_X: #Et on répète l’opération pour une première boite longue…

anc9 = (4.5, 0, 0)
anc10 = (3, 0.5, 0)
anc11 = (-2, 0.5, 0)
anc12 = (-4.5, 0, 0)
anc13 = (-3, -0.5, 0)
anc14 = (2, -0.5, 0)
locationLX = [anc9, anc10, anc11, anc12, anc13, anc14]
#

---------------------------------------------------------------------------------------------------------------# SI_LA_PREMIERE_BOITE_CHOISIS_ETAIT_UNE_COURTE_SUIVANT_X_:

#
def siCourte_X():  # Ici, nous allons intégrer certains paramètres de choix de points d'ancrage dans une fonction...
    for _ in range(
        0, random.randint(1, 4)
    ):  # Boucle permettant de créer un nombre aléatoire de boites autour de la première (minimum 0, le maximum correspond au nombre de points d'ancrage possibles.
        if (
            s == courte_X
        ):  # Si la première boite qui a été générée est une courte suivant X:
            loc = random.choice(
                locationCX
            )  # Alors on choisit parmi les points d'ancrage possibles autour d'une courte X.
            y = random.choice(
                size_Y
            )  # On choisit aléatoirement parmi les barres perpendiculaires à la première, celle qui sera la suivante...
            par = (
                loc + y
            )  # On implémente la position et les dimensions choisies en tant que paramètre d'objet...
            MaBoite(*par)  # On génère la nouvelle boite.
            if y == courte_Y:  # Si la nouvelle boite est une courte suivant y...
                if (
                    loc == anc1
                ):  # Et si elle est au point d'ancrage 1, alors elle est centrée sur ce point d'ancrage, mais nous souhaitons que ce soit son extrémité qui soit au point d'ancrage...
                    deplacement_Y = [
                        0.5,
                        -0.5,
                        1.5,
                        -1.5,
                    ]  # On opère donc un déplacement aléatoire en fonction des différentes positions qu’elle peut prendre sur ce même point d'ancrage...
                    dep = random.choice(deplacement_Y)
                    bpy.ops.transform.translate(value=(0.5, dep, 0))
                    locationCX.remove(
                        anc1
                    )  # On supprime le point d'ancrage que l'on vient d'utiliser dans la liste, pour ne pas en générer une autre au même endroit lors du passage dans la boucle.

                elif loc == anc3:  # Même chose avec le point d'ancrage 3 comme choix.
                    deplacement_Y = [0.5, -0.5, 1.5, -1.5]
                    dep = random.choice(deplacement_Y)
                    bpy.ops.transform.translate(value=(-0.5, dep, 0))
                    locationCX.remove(anc3)
                elif loc == anc2:  # Idem point d'ancrage 2.
                    bpy.ops.transform.translate(value=(0, 2, 0))
                    locationCX.remove(anc2)
                elif loc == anc4:  # Idem point d'ancrage 4.
                    bpy.ops.transform.translate(value=(0, -2, 0))
                    locationCX.remove(anc4)

            elif (
                y == longue_Y
            ):  # Mêmes opérations, mais cette fois-ci avec une barre longue perpendiculaire à la première...
                if loc == anc1:
                    deplacement_Y = [0, 1, 2, 3, 4, -1, -2, -3, -4]
                    dep = random.choice(deplacement_Y)
                    bpy.ops.transform.translate(value=(0.5, dep, 0))
                    locationCX.remove(anc1)
                elif loc == anc3:
                    deplacement_Y = [0, 1, 2, 3, 4, -1, -2, -3, -4]
                    dep = random.choice(deplacement_Y)
                    bpy.ops.transform.translate(value=(-0.5, dep, 0))
                    locationCX.remove(anc3)
                elif loc == anc2:
                    bpy.ops.transform.translate(value=(0, 4.5, 0))
                    locationCX.remove(anc2)
                elif loc == anc4:
                    bpy.ops.transform.translate(value=(0, -4.5, 0))
                    locationCX.remove(anc4)
#

---------------------------------------------------------------------------------------------------------------# SI_LA_PREMIERE_BOITE_CHOISIS_ETAIT_UNE_LONGUE_SUIVANT_X_: Même opération qu’au-dessus, mais avec un choix initial diffèrent… (si la première boite générée est une boite longue…)

#
def siLongue_X():
    for _ in range(0, random.randint(1, 4)):
        if s == longue_X:
            loc = random.choice(locationLX)
            y = random.choice(size_Y)
            par = loc + y
            MaBoite(*par)
            if y == courte_Y:
                if loc == anc9:
                    deplacement_Y = [0.5, 1.5, -0.5, -1.5]
                    dep = random.choice(deplacement_Y)
                    bpy.ops.transform.translate(value=(0.5, dep, 0))
                    locationLX.remove(anc9)
                elif loc == anc12:
                    deplacement_Y = [0.5, 1.5, -0.5, -1.5]
                    dep = random.choice(deplacement_Y)
                    bpy.ops.transform.translate(value=(-0.5, dep, 0))
                    locationLX.remove(anc12)
                elif loc == anc10:
                    bpy.ops.transform.translate(value=(0, 2, 0))
                    locationLX.remove(anc10)
                elif loc == anc11:
                    bpy.ops.transform.translate(value=(0, 2, 0))
                    locationLX.remove(anc11)
                elif loc == anc13:
                    bpy.ops.transform.translate(value=(0, -2, 0))
                    locationLX.remove(anc13)
                elif loc == anc14:
                    bpy.ops.transform.translate(value=(0, -2, 0))
                    locationLX.remove(anc14)

            elif y == longue_Y:
                if loc == anc9:
                    deplacement_Y = [0, 1, 2, 3, 4, -1, -2, -3, -4]
                    dep = random.choice(deplacement_Y)
                    bpy.ops.transform.translate(value=(0.5, dep, 0))
                    locationLX.remove(anc9)
                elif loc == anc12:
                    deplacement_Y = [0, 1, 2, 3, 4, -1, -2, -3, -4]
                    dep = random.choice(deplacement_Y)
                    bpy.ops.transform.translate(value=(-0.5, dep, 0))
                    locationLX.remove(anc12)
                elif loc == anc10:
                    bpy.ops.transform.translate(value=(0, 4.5, 0))
                    locationLX.remove(anc10)
                elif loc == anc11:
                    bpy.ops.transform.translate(value=(0, 4.5, 0))
                    locationLX.remove(anc11)
                elif loc == anc13:
                    bpy.ops.transform.translate(value=(0, -4.5, 0))
                    locationLX.remove(anc13)
                elif loc == anc14:
                    bpy.ops.transform.translate(value=(0, -4.5, 0))
                    locationLX.remove(anc14)
#

---------------------------------------------------------------------------------------------------------------# OPERATIONS_DE_DUPLICATION_ET_ROTATION_POUR_CREER_L’ETAGE_: ---------------------------------------------------------------------------------------------------------------# Précédemment, nous avons créé un élément primaire qui va se répéter sur tout l’étage, il faut donc le positionner, le dupliquer et lui faire subir des rotations pour créer l’étage. On intègre ces paramètres dans une fonction…

#
def etage(hauteur):
    variationsX = [
        6,
        6.5,
        7,
        7.5,
        8,
        8.5,
        9,
        9.5,
        10,
        10.5,
        11,
        11.5,
        12,
        12.5,
        13,
        13.5,
        14,
        14.5,
        15,
        15.5,
        16,
        16.5,
        17,
        17.5,
        18,
        18.5,
        19,
    ]
    variationsY = [
        1,
        1.5,
        2,
        2.5,
        3,
        3.5,
        4,
        4.5,
        5,
        5.5,
        6,
        6.5,
        7,
        7.5,
        8,
        8.5,
        9,
        9.5,
        10,
        10.5,
        11,
        11.5,
        12,
        12.5,
        13,
        13.5,
        14,
    ]
    positionX = random.choice(
        variationsX
    )  # On définit plusieurs positions possibles pour l'élément primaire...
    positionY = random.choice(
        variationsY
    )  # Et on demande d'en choisir une aléatoirement.
    bpy.ops.object.select_all(action="SELECT")  # On selectionne tout
    bpy.ops.transform.translate(
        value=(5, 5, hauteur)
    )  # On positionne l'élément primaire.
    bpy.ops.object.duplicate()  # On duplique

    bpy.ops.group.create()  # On groupe les elements dupliqués
    bpy.ops.object.join()
    bpy.ops.object.origin_set(type="ORIGIN_CURSOR")
    bpy.ops.transform.rotate(
        value=1.5708, constraint_axis=(False, False, True)
    )  # Rotation...

    bpy.ops.object.duplicate()  # Idem.
    bpy.ops.transform.rotate(value=1.5708, constraint_axis=(False, False, True))

    bpy.ops.object.duplicate()  # Idem.
    bpy.ops.transform.rotate(value=1.5708, constraint_axis=(False, False, True))

    bpy.ops.object.select_all(action="SELECT")  # On selectionne tout.
    bpy.ops.transform.translate(
        value=(positionX, positionY, hauteur)
    )  # On positionne le motif avec les listes de positions aléatoires définies plus haut...
    bpy.ops.object.duplicate()  # On duplique.

    bpy.ops.group.create()  # On groupe les elements dupliqués.
    bpy.ops.object.join()
    bpy.ops.object.origin_set(
        type="ORIGIN_CURSOR"
    )  # Puis on fait une nouvelle rotation, pour finalement créer l'étage...
    bpy.ops.transform.rotate(value=1.5708, constraint_axis=(False, False, True))

    bpy.ops.object.duplicate()  # Idem.
    bpy.ops.transform.rotate(value=1.5708, constraint_axis=(False, False, True))

    bpy.ops.object.duplicate()  # Idem.
    bpy.ops.transform.rotate(value=1.5708, constraint_axis=(False, False, True))
#

---------------------------------------------------------------------------------------------------------------# EXECUTION_DES_FONCTIONS: ---------------------------------------------------------------------------------------------------------------# En exécutant toutes les fonctions créées précédemment, on génère le modèle complet…

#
def generationModel(
    calque, calquePrecedent, hauteur
):  # On définit une fonction qui va exécuter toutes les fonctions du code.
    bpy.context.scene.layers[calque] = (
        True  # Et qui change de calque et de hauteur pour chaque étage...
    )
    bpy.context.scene.layers[calquePrecedent] = False
    MaBoite(0, 0, 0, *s)
    siCourte_X()
    siLongue_X()
    etage(hauteur)


generationModel(
    1, 0, 0
)  # On génère l'étage 1 sur le calque 1, en désactivant le calque 0, et en positionnant l'étage à une hauteur 0 (l'étage invariant étant en -0,5)
locationCX = [
    anc1,
    anc2,
    anc3,
    anc4,
]  # On réstaure les listes de points d'ancrages afin de pouvoir les réutiliser.
locationLX = [anc9, anc10, anc11, anc12, anc13, anc14]
generationModel(
    2, 1, 0.5
)  # Génération de l'étage 2 sur le calque 2, en désactivant le calque 1, et à une hauteur de 0,5.
locationCX = [
    anc1,
    anc2,
    anc3,
    anc4,
]  # On réstaure les listes de points d'ancrages afin de pouvoir les réutiliser.
locationLX = [anc9, anc10, anc11, anc12, anc13, anc14]
generationModel(
    3, 2, 1
)  # Génération de l'étage 3 sur le calque 3, en désactivant le calque 2, et à une hauteur de 1.


visibiliteCalques(
    True
)  # On réactive tous les calques, pour enfin admirer le modèle créé !

bpy.ops.object.select_all(
    action="TOGGLE"
)  # On désélectionne tout pour préparer le lancement suivant du code.


print(
    "-------------------------------------------------------------------------------------------------------"
)
#

---------------------------------------------------------------------------------------------------------------# ---------------------------------------------------------------------------------------------------------------#